package com.maxbill.singleton;


import lombok.extern.log4j.Log4j2;

import java.io.*;
import java.lang.reflect.Constructor;

@Log4j2
public class SingletonTest {

    /*
     * 破坏单例模式的三种方式
     * 反射
     * 序列化
     * 克隆
     */


    /*
     * 解决方案如下：防止反射 :定义一个全局变量，当第二次创建的时候抛出异常
     * 防止克隆 :重写clone(),直接返回单例对象
     * 防止序列化:添加readResolve(),返回Object对象
     */


    /**
     * 1) 单例模式保证了 系统内存中该类只存在一个对象，节省了系统资源，对于一些需要频繁创建销毁的对象，使
     * 用单例模式可以提高系统性能
     * 2) 当想实例化一个单例类的时候，必须要记住使用相应的获取对象的方法，而不是使用 new
     * 3) 单例模式 使用的场景：需要 频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即：重量级
     * 对象)，但又经常用到的对象、 工具类对象、频繁访问数据库或文件的对象(比如 数据源、session  工厂等)
     */


    public static void main(String[] args) throws Exception {


//        for (int i = 0; i < 3; i++) {
//            Thread thread = new Thread(() -> {
//                log.info(SingletonHungerMode.getInstance());
//                log.info(SingletonHolderMode.getInstance());
//                log.info(SingletonLazyRiskMode.getInstance());
//                log.info(SingletonLazySafeMode.getInstance());
//                log.info(SingletonEnumMode.INSTANCE.getInstance());
//            });
//            thread.start();
//        }

        log.info("-----------序列化-----------");
        SingletonHungerMode originSingleton = SingletonHungerMode.getInstance();
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(SingletonHungerMode.getInstance());
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        SingletonHungerMode serializeSingleton = (SingletonHungerMode) ois.readObject();
        System.out.println(originSingleton == serializeSingleton);

        log.info("-----------反射-----------");
        Constructor<SingletonHungerMode> cons = SingletonHungerMode.class.getDeclaredConstructor();
        cons.setAccessible(true);
        SingletonHungerMode reflectSingleton = cons.newInstance();
        System.out.println(reflectSingleton == originSingleton);

        log.info("-----------克隆-----------");
        SingletonHungerMode cloneSingleton = (SingletonHungerMode) originSingleton.clone();
        System.out.println(cloneSingleton == originSingleton);
    }

}
